/**
**************************************************************************************
*Founder Effect                                                                      *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include "Kernel.h"

#include <iostream>
#include <stdio.h>

// max number of frames for the Game Module to 
// skip ahead
const int MAX_FRAMESKIP = 15;


Kernel::Kernel(void)
{
    m_delta = 0.0f;
    m_lastFrame = 0.0f;
    m_nextTick = 0.0f;
    m_quit = false;
}
/** Init the kernel */
int Kernel::Init()
{
    // Init SDL
    if(SDL_Init(SDL_INIT_TIMER) < 0){ 
        std::cerr << "***CRITICAL ERROR::Could not Initialize SDL"; 
        return 1;
    }

    m_cvars.Init();
    if ( m_console.Init() )
    {
        std::cerr << "***CRITICAL ERROR::Could not Initialize the command line interface!\n";
        return 1;
    }

    //Now that we're enabled, make sure we cleanup
    atexit(SDL_Quit); 
    return 0;
}
 
/** Shutdown all Processes */
void Kernel::Shutdown()
{
    // Kill all the processes
    KillAll();

    if ( m_game ) {
        m_game->Shutdown();
        delete m_game;
    }

    if ( m_scripter ) {
        m_scripter->Shutdown();
        delete m_scripter;
    }

    if ( m_resource ) {
        m_resource->Shutdown();
        delete m_resource;
    }

    if ( m_sound ) {
        m_sound->Shutdown();
        delete m_sound;
    }
    if ( m_renderer ) {
        m_renderer->Shutdown();
        delete m_renderer;
    }

    if ( m_input ) {
        m_input->Shutdown();
        delete m_input;
    }

    // close sdl
    SDL_Quit();
}

/** Run the kernel */
void Kernel::Execute()
{   
    m_nextTick = GetTime();
    while( !m_quit )
    {
        Frame();
    }


  //  // Execute all processes in order
  //  // of priority
  //  while(m_runningProcesses.size())
  //  {
  //      const float timeStep = m_cvars.GetFloatValue("v_maxfps");
  //      const float SIXTY_HERTZ = 1.0f / timeStep;
  //      const float SKIP_TICKS  = 1000.0f / timeStep;
  //      
  //      // calculate how long it was since we last came round this loop
  //      // and hold on to it so we can let the updating/rendering routine
  //      // know how much time to update by
  //      //m_delta = (long)(GetTime() - m_lastFrame);
  //      m_lastFrame = GetTime();

  //      m_percisionTimer.Refresh();
		//type_ProcessList::iterator it;
		//for(it=m_runningProcesses.begin(); it!=m_runningProcesses.end(); ++it)
		//{
		//	IProcess* t = (*it);		
  //          if(!t->CanKill() && t->IsActive() ) {
  //              if ( dynamic_cast< GameManager* >(t) ) {
  //                  loops = 0;
  //                  
  //                  while( m_lastFrame > nextTick && loops < MAX_FRAMESKIP ) {
  //                      t->Update( SIXTY_HERTZ );
  //                      nextTick += SKIP_TICKS;
  //                      loops++;
  //                  }
  //              }
  //              else {
  //                  t->Update( GetFrameTime() );
  //              }
  //          }
		//}

		//// loop again to remove dead tasks
  //      it = m_runningProcesses.begin();
  //      while( it != m_runningProcesses.end() )
		//{
		//	IProcess* t = (*it);
		//	if(t->CanKill())
		//	{
  //              t->Shutdown();
		//		it = m_runningProcesses.erase( it );
  //              delete t; t = 0;
		//	}
  //          else ++it;
		//}
  //  }
}


/** Run a Frame */
void Kernel::Frame()
{
    m_lastFrame = GetTime();
    m_percisionTimer.Refresh(); 

    /* Max time step */
    const float timeStep = m_cvars.GetFloatValue("v_maxfps");
    const float SIXTY_HERTZ = 1.0f / timeStep;
    const float SKIP_TICKS  = 1000.0f / timeStep;

    float deltaTime = GetFrameTime();

    m_input->Update( deltaTime );
    // Catch up on the physics updates if needed,
    // have a constant deltaTime
    //int loops = 0;
    //while( m_lastFrame > m_nextTick && loops < MAX_FRAMESKIP ) {
        m_game->Update( deltaTime/1000.0f/*SIXTY_HERTZ*/ );

    //    m_nextTick += SKIP_TICKS;
    //    loops++;
    //}
    m_sound->Update( deltaTime );
    m_renderer->Update( deltaTime );
}

/**
=======================
Get the current time 
=======================
*/
float Kernel::GetTime() 
{
    return (float)SDL_GetTicks();
}

/**
========================
Get the FrameTime - frametime is one game loop
========================
*/
float Kernel::GetFrameTime()
{
    return m_percisionTimer.GetFrameTime()*1000.0f;
}

/** Print to the console */
void Kernel::Printf( const char *s, ... )
{
    char buffer[1024]; 
	va_list arguments;
    // parse out the arguments, and put them in 
    // string format
	va_start(arguments, s);
	    _vsnprintf_s(buffer, sizeof(buffer), s, arguments);
	    //vsprintf(buffer, s, arguments);
    va_end(arguments);

    m_console.Print( buffer );
}

/** Print to the console and log the contents */
void Kernel::LPrintf( const char *s, ... )
{
    char buffer[1024]; 
	va_list arguments;
    // parse out the arguments, and put them in 
    // string format
	va_start(arguments, s);
	     _vsnprintf_s(buffer, sizeof(buffer), s, arguments);
	    //vsprintf(buffer, s, arguments);
    va_end(arguments);

    bool status = m_console.IsLogging();
    m_console.SetLog( true );
        m_console.Print( buffer );
    m_console.SetLog( status );
}

/** Add a process based off priority */
void Kernel::AddProcess(IProcess* p)
{
	// keep the order of priorities straight
	type_ProcessList::iterator it;
	for(it=m_runningProcesses.begin();it!=m_runningProcesses.end();it++)
	{
		IProcess* comp=(*it);
		// if this priority is greater,
        // we want to insert it now
        if(comp->GetPriority() > p->GetPriority() )
            break;
	}
    // insert the process
	m_runningProcesses.insert(it,p);
}

/** Suspend a process */
void Kernel::SuspendProcess(IProcess* p)
{
	//check that this task is in our list - we don't want to suspend a task that isn't running
	if(std::find( m_runningProcesses.begin(), m_runningProcesses.end(), p) != m_runningProcesses.end())
	{
		m_runningProcesses.remove(p);
		m_pausedProcesses.push_back(p);
	}
}

/** Resume a suspended process */
void Kernel::ResumeProcess(IProcess* p)
{
    if(std::find( m_pausedProcesses.begin(), m_pausedProcesses.end(), p ) != m_pausedProcesses.end() )
    {
        m_pausedProcesses.remove(p);
        // insert in the proper order
        type_ProcessList::iterator it;
        for ( it = m_runningProcesses.begin(); it != m_runningProcesses.end(); it++ )
        {
            if ( (*it)->GetPriority() > p->GetPriority() )
                break;
        }
        // insert in properly
        m_runningProcesses.insert( it, p );
    }

}

/** Kill a process */
void Kernel::RemoveProcess(IProcess* p)
{
    // Kill the process
    if( std::find( m_runningProcesses.begin(), m_runningProcesses.end(), p) != m_runningProcesses.end() )
        p->SetKill( true );
}

/** Kill all the processes */
void Kernel::KillAll()
{
    type_ProcessList::iterator it;
    for ( it = m_runningProcesses.begin(); it != m_runningProcesses.end(); ++it )
        (*it)->SetKill( true );
   
    // also destroy any paused ones
    for ( it = m_pausedProcesses.begin(); it != m_pausedProcesses.end(); ++it )
        (*it)->SetKill( true );


    m_quit = true;
}

Kernel::~Kernel(void)
{
}
